package com.example.xmltest.test;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.example.xmltest.dto.TeleSignDoSignAdditionalItem;
import com.example.xmltest.dto.TeleSignDoSignDTO;
import com.example.xmltest.dto.TeleSignDoSignStatusItem;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;

/**
 * TeleSign验签
 */
public class TeleSignMain {

    private static final String HMAC_JAVA_ALG = "HmacSHA256";

    public static boolean verifyPOST(String tsApiKey,
                                     Map<String, String> httpHeaders, byte[] responseBody)
            throws NoSuchAlgorithmException, InvalidKeyException {

        byte[] decodedApiKey = Base64.getDecoder().decode(tsApiKey);

        SecretKey key = new SecretKeySpec(decodedApiKey, HMAC_JAVA_ALG);
        Mac mac;
        mac = Mac.getInstance(HMAC_JAVA_ALG);
        mac.init(key);

        // Expected format of Authorization header is "TSA {Customer ID}:{base64 encoded signature}"
        String[] authParts = httpHeaders.get("Authorization").split("[ :]");
        byte[] digest1 = Base64.getDecoder().decode(authParts[2]);
        byte[] digest2 = mac.doFinal(responseBody);

        System.out.printf("Given Signature:    %s\n", authParts[2]);
        System.out.printf("Expected Signature: %s\n", Base64.getEncoder().encodeToString(digest2));

        /*
         * Do an explicit disjunction of the bytes, since MessageDigest.isEqual apparently
         * short-circuits according to http://codahale.com/a-lesson-in-timing-attacks/.
         */
        if (digest1.length != digest2.length) {
            return false;
        }
        int res = 0;

        for (int i = 0; i < digest1.length; i++) {
            res |= (digest1[i] ^ digest2[i]);
        }
        return res == 0;
    }

    public static void main(String[] args) throws ParseException {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String customerId = "5F738B84-5D43-4BD6-9C03-775C809C80F3";
        String apiKey = "/81Z6aQ83uLgFTv1ugWSjtmCuv7pfNG/C90czkyNecFs+laHj6frULYNrIO7epqYMoUqPziyRJxDudoPXlcnrA==";
        String expectedSignature = "vjumyki8+4OnX0VR0JdAhc6puvEsLnogs48qPepDcrU=";
        TeleSignDoSignDTO teleSignDoSignDTO = new TeleSignDoSignDTO();
        TeleSignDoSignAdditionalItem item = new TeleSignDoSignAdditionalItem();
        item.setMessage_parts_count(3);
        TeleSignDoSignStatusItem statusItem = new TeleSignDoSignStatusItem();
        statusItem.setCode(200);
        statusItem.setDescription("Delivered to handset");
        statusItem.setUpdated_on("2022-07-08T12:51:41.915947Z");
        teleSignDoSignDTO.setAdditional_info(item);
        teleSignDoSignDTO.setStatus(statusItem);
        teleSignDoSignDTO.setReference_id("36077776B2B405689190732A43D5434E");
        System.out.println(JSON.toJSONString(teleSignDoSignDTO, SerializerFeature.WriteMapNullValue));

        byte[] responseBody = JSON.toJSONString(teleSignDoSignDTO, SerializerFeature.WriteMapNullValue).getBytes();//true
        Object parse = JSONObject.parse(responseBody);
        System.out.println("parse:"+parse);
//        String jsonString = "{\"additional_info\":{\"message_parts_count\":3},\"external_id\":null,\"reference_id\":\"3607775BFC900C68919151F37A853FBD\",\"status\":{\"code\":200,\"description\":\"Delivered to handset\",\"updated_on\":\"2022-07-08T12:44:24.341577Z\"}}";
//        byte[] responseBody1 = JSONObject.toJSONBytes(jsonString);//KdtjZEjUx67ABpup1yhOQPZbJ3I23XPH8AzIUijqlU8=
//        Object parse1 = JSONObject.parse(responseBody1);
//        System.out.println("parse1:"+parse1);
        byte[] responseBody2 = objectToByteArray(teleSignDoSignDTO);//hpIGtBuhPMn1ijVeFNWZAreq8KQpFnpO/V/+GDMaLYc=
        Object parse2 = JSONObject.parse(responseBody2);
        System.out.println("parse2:"+parse2);
//        byte[] responseBody = new byte[] { };

        Map<String, String> httpHeaders = new HashMap<String, String>(){{
            put("Authorization", "TSA " + customerId + ":" + expectedSignature);
        }};

        boolean verified = false;

        try {
            verified = verifyPOST(apiKey, httpHeaders, responseBody);
        } catch (Exception exc) {
            System.out.printf("Exception thrown by verifyPOST %s\n", exc.toString());
        }

        if (verified) {
            System.out.println("Signature verified.");
        }
        else {
            System.out.println("Signature is invalid!");
        }
    }

    public static byte[] objectToByteArray(TeleSignDoSignDTO obj) {
        byte[] bytes = null;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(obj);
            oos.flush();
            bytes = bos.toByteArray ();
//            Object parse3 = JSONObject.parse(bytes);
//            System.out.println("parse3:"+parse3);
            oos.close();
            bos.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        return bytes;
    }
}